﻿using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;



namespace MyDX
{

    public class MyBoundingBox
    {
        public Vector3[] Vert;
        public MyBoundingBox(Vector3 LL, Vector3 UR)
        {
            Vert = new Vector3[8];
            Vert[0] = new Vector3(LL.X,LL.Y,LL.Z);
            Vert[1] = new Vector3(LL.X, LL.Y, UR.Z);
            Vert[2] = new Vector3(UR.X, LL.Y, UR.Z);
            Vert[3] = new Vector3(UR.X, LL.Y, LL.Z);
            Vert[4] = new Vector3(LL.X, UR.Y, LL.Z);
            Vert[5] = new Vector3(LL.X, UR.Y, UR.Z);
            Vert[6] = new Vector3(UR.X, UR.Y, UR.Z);
            Vert[7] = new Vector3(UR.X, UR.Y, LL.Z);
        }

        private void MulByMatrix(Matrix M)
        {
            Vert = Vector3.TransformCoordinate(Vert,M);
        }

        public MyBoundingBox Transform(Matrix M)
        {
            MyBoundingBox RV = new MyBoundingBox(Vert[0], Vert[6]);
            RV.MulByMatrix(M);
            return RV;
        }
      

    }





    public class MyCamera
    {
        private Vector3 CP, CU;

        public Vector3 Up
        {
            get { return CU; }
            set { CU = value; }
        }

        public Vector3 Position
        {
            get { return CP; }
            set { CP = value; }
        }

        public MyCamera()
        {
            CP = new Vector3(0, 0, 0);
            CU = new Vector3(0, 0, 1);
        }

       
        public MyCamera(Vector3 InitialPosition, Vector3 InitialUp)
        { 
            
            CP = InitialPosition;
            CU = InitialUp;
            
        }

        public Matrix ViewMatrix(Vector3 LookAt)
        {

            
            return Matrix.LookAtLH(CP, LookAt, CU);
            

        }

        public void TransformPosition(Matrix m)
        {
            Vector4 V = Vector3.Transform(CP, m);
            CP = new Vector3(V.X, V.Y, V.Z);
        }
        
        public void TransformUp(Matrix m)
        {
            Vector4 V = Vector3.Transform(CU, m);
            CU = new Vector3(V.X, V.Y, V.Z);
        }

    }
    public struct MyVertex
    {
       public float x, y, z;
    }

    public class MyObject
    {
        private Mesh m;
        private Device dev;
        private ExtendedMaterial[] ExtendedMaterials;
        private Texture[] Textures;
        private Material[] Materials;
        public Vector3 center;
        public MyBoundingBox BBox;
        public float radius;

        public Vector3 Position;
        public Quaternion Rotation;
        public VertexDeclaration vd;
        public VertexBuffer vb;

        public MyObject BoundingBox(ref Device d)
        {
            MyObject RV = new MyObject(ref d);
            RV.m = new Mesh(12, 8, MeshFlags.Managed|MeshFlags.Use32Bit, VertexFormats.Position, d);

            MyVertex[] vert = new MyVertex[8];
            for (int i = 0; i < 8; i++)
            {
                vert[i].x = BBox.Vert[i].X;
                vert[i].y = BBox.Vert[i].Y;
                vert[i].z = BBox.Vert[i].Z;
            }
            int[] Faces = { 0, 4, 3, 3, 4, 7, 1, 2, 5, 2, 6, 5, 3, 7, 2, 2, 7, 6, 0, 1, 4, 4, 1, 5, 4, 5, 7, 7, 5, 6, 0, 3, 1, 1, 3, 2 };
            RV.m.SetVertexBufferData(vert, LockFlags.None);
            RV.m.SetIndexBufferData(Faces, LockFlags.None);
            RV.NormalizeVB();
            RV.Position = Position;
            RV.Rotation = Rotation;
            RV.ExtendedMaterials = new ExtendedMaterial[1];
            RV.CalculateBounds();
      
            return RV;
        }

        public MyObject BoundingBox(ref Device d, MyBoundingBox B)
        {
            MyObject RV = new MyObject(ref d);
            RV.m = new Mesh(12, 8, MeshFlags.Managed | MeshFlags.Use32Bit, VertexFormats.Position, d);

            MyVertex[] vert = new MyVertex[8];
            for (int i = 0; i < 8; i++)
            {
                vert[i].x = B.Vert[i].X;
                vert[i].y = B.Vert[i].Y;
                vert[i].z = B.Vert[i].Z;
            }
            int[] Faces = { 0, 4, 3, 3, 4, 7, 1, 2, 5, 2, 6, 5, 3, 7, 2, 2, 7, 6, 0, 1, 4, 4, 1, 5, 4, 5, 7, 7, 5, 6, 0, 3, 1, 1, 3, 2 };
            RV.m.SetVertexBufferData(vert, LockFlags.None);
            RV.m.SetIndexBufferData(Faces, LockFlags.None);
            RV.NormalizeVB();
            RV.Position = Position;
            RV.Rotation = Rotation;
            RV.ExtendedMaterials = new ExtendedMaterial[1];
            RV.CalculateBounds();

            return RV;
        }

        public MyObject(ref Device d)
        {
            m = null;
            ExtendedMaterials = null;
            Textures = null;
            Materials = null;
            dev = d;
         }

        public void CalculateBounds()
        {
            VertexBuffer vert = m.VertexBuffer;
            Vector3 BoxMin, BoxMax;
            GraphicsStream stream = vert.Lock(0, 0, LockFlags.None);
            radius = Geometry.ComputeBoundingSphere(stream, m.NumberVertices, m.VertexFormat, out center);
            Geometry.ComputeBoundingBox(stream, m.NumberVertices, m.VertexFormat, out BoxMin, out BoxMax);
            vert.Unlock();
            BBox = new MyBoundingBox(BoxMin, BoxMax);
        }

        public unsafe void Normalize(Matrix PreRotation)  // This method is a dirty hack. It can burn your PC and kill your cat.
        {
            int VertexSize = m.VertexBuffer.Description.Size/m.NumberVertices;
            CalculateBounds();
            
            GraphicsStream data=m.VertexBuffer.Lock(0,m.VertexBuffer.SizeInBytes,LockFlags.None);
            byte  *b = (byte *) data.InternalDataPointer;
            float *f;
            Vector3 V;
            for (int i = 0; i < m.NumberVertices; i++)
            {
                f = (float*)(b + VertexSize * i);
                V = new Vector3(f[0],f[1],f[2]);
                V -= center;
                V.Scale(1/radius);
                V.TransformCoordinate(PreRotation);
                f[0] = V.X;
                f[1] = V.Y;
                f[2] = V.Z;
 
            }
            
            m.VertexBuffer.Unlock();
            CalculateBounds();

        }

        public void NormalizeVB()
        {
            //m = m.Clone(m.Options.Value, CustomVertex.PositionNormalTextured.Format, dev);
            //m.ComputeNormals();
            vd = new VertexDeclaration(dev, m.Declaration);
            vb = m.VertexBuffer;
        }


        public void Render(ref Effect effect, string xTextureName)
        {
            dev.VertexFormat = m.VertexFormat;
            
            for (int i = 0; i < ExtendedMaterials.Length; i++)
            {
                if (xTextureName != null)
                {
                    effect.SetValue(xTextureName, Textures[i]);
                   
                    effect.CommitChanges();
                }
                m.DrawSubset(i);
            }
            
        }


        public void RotateY(float angle)
        {
            Vector3 TempVec = new Vector3(0,  1, 0);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X,V.Y,V.Z), angle);
            Rotation*=TempRot;
        }

        public void RotateX(float angle)
        {
            Vector3 TempVec = new Vector3(1, 0, 0);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X, V.Y, V.Z), angle);
            Rotation *= TempRot;
        }

        public void RotateZ(float angle)
        {
            Vector3 TempVec = new Vector3(0, 0, 1);
            Vector4 V = Vector3.Transform(TempVec, Matrix.RotationQuaternion(Rotation));
            Quaternion TempRot = Quaternion.RotationAxis(new Vector3(V.X, V.Y, V.Z), angle);
            Rotation *= TempRot;
        }

        public void CreateTerrainFromFile(float mappingfactor, float StepX, float StepY, float StepZ, string Filename)
        {
            Bitmap b = new Bitmap(Filename);

            int NoX = b.Size.Width;
            int NoY = b.Size.Height;
            int NoV = NoX * NoY;
            int NoF = (NoX - 1) * (NoY - 1) * 2 * 3;
            CustomVertex.PositionNormalTextured[] vertices = new CustomVertex.PositionNormalTextured[NoV];
            int k = 0;
            for (int i = 0; i < NoX; i++)
                for (int j = 0; j < NoY; j++)
                {
                    vertices[k].Position = new Vector3(i * StepX - (NoX / 2) * StepX, j * StepY - (NoY / 2) * StepY, b.GetPixel(i, j).R * StepZ);
                    vertices[k].Tu = mappingfactor*i / (float)(NoX - 1);
                    vertices[k].Tv = mappingfactor*j / (float)(NoY - 1);
                    vertices[k].Normal = new Vector3(0, 0, 0);
                    k++;
                }
            int[] indices = new int[NoF];

            k = 0;
            for (int i = 1; i < NoX; i++)
                for (int j = 1; j < NoY; j++)
                {
                    indices[k++] = (i - 1) * NoY + j;
                    indices[k++] = (i - 1) * NoY + j - 1;
                    indices[k++] = i * NoY + j - 1;
                    indices[k++] = (i - 1) * NoY + j;
                    indices[k++] = i * NoY + j - 1;
                    indices[k++] = i * NoY + j;
                }

            m = new Mesh(NoF, NoV, MeshFlags.Managed | MeshFlags.Use32Bit, CustomVertex.PositionNormalTextured.Format, dev);


            m.IndexBuffer.SetData(indices, 0, LockFlags.None);
            m.VertexBuffer.SetData(vertices, 0, LockFlags.None);
            m.ComputeNormals();
            CalculateBounds();
            ExtendedMaterials = new ExtendedMaterial[1];
            Textures = new Texture[1];
            Materials = new Material[1];
            ExtendedMaterials[0] = new ExtendedMaterial();
            Textures[0] = null;
            Materials[0] = new Material();
            Materials[0].Ambient = Color.White;
            Materials[0].Diffuse = Color.White;


        }

        public void CreateQuadrangle(int Width, int Height)
        {
            CustomVertex.PositionNormalTextured[] vertices = new CustomVertex.PositionNormalTextured[4];
            int i = 0;
            vertices[i].Position = new Vector3(Width/2, 0f, -Height/2);
            vertices[i].Tu = 0f;
            vertices[i++].Tv = 1f;
            vertices[i].Position = new Vector3(Width/2, 0f, Height/2);
            vertices[i].Tu = 0f;
            vertices[i++].Tv = 0f;
            vertices[i].Position = new Vector3(-Width/2, 0f, -Height/2);
            vertices[i].Tu = 1f;
            vertices[i++].Tv = 1f;
            vertices[i].Position = new Vector3(-Width/2, 0f, Height/2);
            vertices[i].Tu = 1f;
            vertices[i++].Tv = 0f;
            m = new Mesh(6, 4, MeshFlags.Managed | MeshFlags.Use32Bit, CustomVertex.PositionNormalTextured.Format, dev);
            i = 0;
            int[] indices = new int[6];
            indices[i++] = 0;
            indices[i++] = 2;
            indices[i++] = 1;
            indices[i++] = 1;
            indices[i++] = 2;
            indices[i++] = 3;
            m.IndexBuffer.SetData(indices, 0, LockFlags.None);
            m.VertexBuffer.SetData(vertices, 0, LockFlags.None);
            m.ComputeNormals();
            CalculateBounds();
            ExtendedMaterials = new ExtendedMaterial[1];
            Textures = new Texture[1];
            Materials = new Material[1];
            ExtendedMaterials[0] = new ExtendedMaterial();
            Textures[0] = null;
            Materials[0] = new Material();
            Materials[0].Ambient = Color.White;
            Materials[0].Diffuse = Color.White;
        }


        public bool TerrainCollision(Vector3 V)
        {
            
            return m.Intersect(V,new Vector3(0,0,1.0f));
        }

        public bool TerrainCollision( MyObject O)
        {

            MyBoundingBox B = O.BBox.Transform(Matrix.Identity);

            Matrix M = Matrix.RotationQuaternion(Rotation) * Matrix.Translation(Position);
            M.Invert();
            M = Matrix.RotationQuaternion(O.Rotation) * Matrix.Translation(O.Position) * M;

            B = B.Transform(M);

            for (int i = 0; i < 8; i++)
                if (TerrainCollision(B.Vert[i])) return true;
            return false;
        }

        public bool IsInside(Vector3 V)
        {
            IntersectInformation[] II;
            //m.Intersect(V, new Vector3(1, 0, 0), out II);
            //if (II.Length%2== 0) return false;
            m.Intersect(V, new Vector3(0, 1, 0), out II);
            if (II.Length%2 == 0) return false;
           // m.Intersect(V, new Vector3(0, 0, 1), out II);
            //if (II.Length%2 == 0) return false;
            return true;
        }

        public bool ObjectCollision( MyObject O)
        {

            MyBoundingBox B = O.BBox.Transform(Matrix.Identity);

            Matrix M=Matrix.RotationQuaternion(Rotation) * Matrix.Translation(Position);
            M.Invert();
            M= Matrix.RotationQuaternion(O.Rotation) * Matrix.Translation(O.Position)*M;

            B = B.Transform(M);
            
            

            for (int i = 0; i < 8; i++)
                if (IsInside(B.Vert[i])) return true;
            return false;
        }

        public void LoadTexture(int Nr, string FileName)
        {
            if ((Textures != null) && (Textures.Length > Nr))
                Textures[Nr] = TextureLoader.FromFile(dev, FileName);
        }

        public void LoadFromFile(string FileName)
        {
            m = Mesh.FromFile(FileName, MeshFlags.Managed, dev, out ExtendedMaterials);

            CalculateBounds();
            if ((ExtendedMaterials != null) && (ExtendedMaterials.Length > 0))
            {
                Textures = new Texture[ExtendedMaterials.Length];
                Materials = new Material[ExtendedMaterials.Length];
                for (int i = 0; i < ExtendedMaterials.Length; i++)
                {
                    Materials[i] = ExtendedMaterials[i].Material3D;
                    Materials[i].Ambient = Materials[i].Diffuse;
                    if ((ExtendedMaterials[i].TextureFilename != null) && (ExtendedMaterials[i].TextureFilename != string.Empty))
                    {
                        Textures[i] = TextureLoader.FromFile(dev, ExtendedMaterials[i].TextureFilename);
                    }
                }
            }
        }
    }

}

